package dev.ece.suit.loader.models.fbx;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import dev.ece.suit.loader.Joint;
import dev.ece.util.lib.Matrix4f;

public class FBXJoint implements Joint<Float> {

	public static final String DEFORMER = "Deformer";
	
	public static final String CLUSTER = "Cluster";
	
	public static final String INDEXES = "Indexes";
	
	public static final String WEIGHTS = "Weights";
	
	public static final String TRANSFORM = "Transform";
	
	public static final String TRANSFORM_LINK = "TransformLink";
	
	private long id;
	
	private int[] indice;
	
	private float[] weights;
	
	private FBXModel model;
	
	private FBXPoseNode poseNode;
	
	private Matrix4f transform = new Matrix4f();
	private Matrix4f offsetMatrix = new Matrix4f();
	
	private Matrix4f finalMatrix = new Matrix4f();
	private Matrix4f normalMatrix = new Matrix4f();
	
	private FBXJoint() {}
	
	public static FBXJoint[] parseFBXJoint(List<FBXElement> elements) throws IOException {
		if(elements != null && !elements.isEmpty()) {
			List<FBXElement> clusters = findClusterElements(elements);
			FBXJoint[] joints = new FBXJoint[clusters.size()];
			FBXElement cluster = null;
			FBXElement ielement = null;
			FBXElement welement = null;
			FBXElement telement = null;
			FBXElement tlelement = null;
			for(int i=0; i<joints.length; i++) {
				joints[i] = new FBXJoint();
				cluster = clusters.get(i);
				joints[i].id = (Long) cluster.getProperties()[0].getValue();
				ielement = cluster.findFirstChild(INDEXES);
				welement = cluster.findFirstChild(WEIGHTS);
				telement = cluster.findFirstChild(TRANSFORM);
				tlelement = cluster.findFirstChild(TRANSFORM_LINK);
				joints[i].indice = ielement.getProperties()[0].toIndice();
				joints[i].weights = welement.getProperties()[0].toFloatArray();
				joints[i].getTransform().set(telement.getProperties()[0].toMatrix4f());
				joints[i].getOffsetTransform().mul(
						tlelement.getProperties()[0].toMatrix4f().invert(), 
						joints[i].getTransform());
				joints[i].getTransform().invert();
			}
			return joints;
		}
		return null;
	}
	
	public static void setup(FBXJoint[] joints) {
		if(joints != null && joints.length > 0) {
			for(FBXJoint joint: joints) {
				joint.setup();
			}
		}
	}
	
	protected void setup() {
		getGlobalTransform().set(this.getModel().getGlobalMatrix());
		getFinalTransform().set(getGlobalTransform());
		getNormalTransform().set(getFinalTransform());
		getNormalTransform().invert().transpose();
	}
	
	@Override
	public void update(Float current) {
		if(this.hasKeyFrames()) {
			//最终矩阵
//			this.getFinalTransform().mul(getTransform(), getGlobalTransform());
//			this.getFinalTransform().mul(getOffsetTransform());
			this.getFinalTransform().mul(getGlobalTransform(), getOffsetTransform());
			//法线矩阵
			this.getNormalTransform().set(getFinalTransform());
			this.getNormalTransform().invert().transpose();
		}
	}
	
	public static List<FBXElement> findClusterElements(List<FBXElement> elements) {
		List<FBXElement> result = new ArrayList<FBXElement>();
		for(FBXElement element: elements) {
			if(StringUtils.equals(CLUSTER, (String) element.getProperties()[2].getValue())) {
				result.add(element);
			}
		}
		return result;
	}
	
	public static Map<Long, FBXJoint> toFBXJointMap(FBXJoint[] joints) {
		Map<Long, FBXJoint> map = new ConcurrentHashMap<Long, FBXJoint>();
		if(joints != null) {
			for(FBXJoint joint: joints) {
				map.put(joint.getId(), joint);
			}
		}
		return map;
	}
	
	public long getId() {
		return id;
	}

	public int[] getIndice() {
		return indice;
	}

	public float[] getWeights() {
		return weights;
	}

	@Override
	public boolean hasKeyFrames() {
		return this.model != null;
	}

	@Override
	public Joint<Float> getParent() {
		return null;
	}

	public boolean hasParentModel() {
		return this.model != null && this.model.hasParent();
	}
	
	public FBXModel getParentModel() {
		return hasParentModel() ? this.model.getParent() : null;
	}
	
	@Override
	public String getName() {
		return this.model != null ? this.model.getName() : null;
	}

	public FBXModel getModel() {
		return model;
	}

	protected void setModel(FBXModel model) {
		this.model = model;
	}

	public FBXPoseNode getPoseNode() {
		return poseNode;
	}

	protected void setPoseNode(FBXPoseNode poseNode) {
		this.poseNode = poseNode;
	}

	public Matrix4f getTransform() {
		return transform;
	}

	@Override
	public Matrix4f getLocalTransform(Float current) {
		return null;
	}

	@Override
	public Matrix4f getGlobalTransform() {
		return this.getModel().getGlobalMatrix();
	}

	@Override
	public Matrix4f getOffsetTransform() {
		return offsetMatrix;
	}

	@Override
	public Matrix4f getFinalTransform() {
		return finalMatrix;
	}

	@Override
	public Matrix4f getNormalTransform() {
		return normalMatrix;
	}

}
